home *** CD-ROM | disk | FTP | other *** search
- /* FTP client (interactive user) code */
- #include <stdio.h>
- #include "global.h"
- #include "mbuf.h"
- #include "netuser.h"
- #include "icmp.h"
- #include "timer.h"
- #include "tcp.h"
- #include "ftp.h"
- #include "session.h"
- #include "cmdparse.h"
- #ifdef ATARI_ST
- # ifdef __TURBOC__
- # include <tos.h>
- # else
- # include <osbind.h>
- # endif
- #endif
-
- extern struct session *current;
- extern char nospace[];
- extern char badhost[];
- static char notsess[] = "Not an FTP session!\n";
- static char cantwrite[] = "Can't write %s\n";
- static char cantread[] = "Can't read %s\n";
-
- #if (defined(MSC) || (defined(ATARI_ST) && defined(__TURBOC__)))
- static int sndftpmsg(struct ftp *ftp,char *fmt,...);
- #endif
-
- int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),doabort(),
- domkdir(),dormdir(),dotput();
-
- struct cmds ftpabort[] = {
- "", donothing, 0, NULLCHAR, NULLCHAR,
- "abort", doabort, 0, NULLCHAR, NULLCHAR,
- NULLCHAR, NULLFP, 0, "Only valid command is \"abort\"", NULLCHAR
- };
-
- struct cmds ftpcmds[] = {
- "", donothing, 0, NULLCHAR, NULLCHAR,
- "cd", doftpcd, 2, "<directory>", NULLCHAR,
- "dir", dolist, 0, NULLCHAR, NULLCHAR,
- "list", dolist, 0, NULLCHAR, NULLCHAR,
- "get", doget, 2, "<remotefile> [<localfile>]", NULLCHAR,
- "ls", dols, 0, NULLCHAR, NULLCHAR,
- "mkdir", domkdir, 2, "<directory>", NULLCHAR,
- "nlst", dols, 0, NULLCHAR, NULLCHAR,
- "rmdir", dormdir, 2, "<directory>", NULLCHAR,
- "put", doput, 2, "<localfile> [<remotefile>]", NULLCHAR,
- "type", dotype, 0, NULLCHAR, NULLCHAR,
- #if (defined(MSDOS))
- "tput", dotput, 2, "<localspec>", NULLCHAR,
- #endif
- #if (defined(ATARI_ST))
- "tput", dotput, 2, "<localspec> [x]", NULLCHAR,
- #endif
- NULLCHAR, NULLFP, 0, NULLCHAR, NULLCHAR
- };
-
- /* Handle top-level FTP command */
- doftp(argc,argv)
- int argc;
- char *argv[];
- {
- int32 resolve();
- int ftpparse();
- void ftpccr(),ftpccs();
- struct session *s;
- struct ftp *ftp,*ftp_create();
- struct tcb *tcb;
- struct socket lsocket,fsocket;
- char tos = 0;
-
- lsocket.address = ip_addr;
- lsocket.port = lport++;
- if((fsocket.address = resolve(argv[1])) == 0){
- printf(badhost,argv[1]);
- return 1;
- }
- if(argc < 3)
- fsocket.port = FTP_PORT;
- else {
- if((fsocket.port = atoi(argv[2])) == 0)
- fsocket.port = FTP_PORT;
- tos = get_tos(argv[3]);
- }
- /* Allocate a session control block */
- if((s = newsession()) == NULLSESSION){
- printf("Too many sessions\n");
- return 1;
- }
- current = s;
- if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
- strcpy(s->name,argv[1]);
- s->type = FTP;
- s->parse = ftpparse;
-
- /* Allocate an FTP control block */
- if((ftp = ftp_create(0)) == NULLFTP){
- s->type = FREE;
- printf(nospace);
- return 1;
- }
- ftp->state = COMMAND_STATE;
- s->cb.ftp = ftp; /* Downward link */
- ftp->session = s; /* Upward link */
-
- /* Now open the control connection */
- tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
- 0,ftpccr,NULLVFP,ftpccs,tos,(char *)ftp);
- ftp->control = tcb;
- go();
- return 0;
- }
- /* Parse user FTP commands */
- int
- ftpparse(line,len)
- char *line;
- int16 len;
- {
- struct mbuf *bp;
-
- if(current->cb.ftp->state != COMMAND_STATE){
- /* The only command allowed in data transfer state is ABORT */
- if(cmdparse(ftpabort,line) == -1){
- printf("Transfer in progress; only ABORT is acceptable\n");
- }
- fflush(stdout);
- return;
- }
-
- /* Save it now because cmdparse modifies the original */
- bp = qdata(line,len);
-
- if(cmdparse(ftpcmds,line) == -1){
- /* Send it direct */
- if(bp != NULLBUF)
- send_tcp(current->cb.ftp->control,bp);
- else
- printf(nospace);
- } else {
- free_p(bp);
- }
- fflush(stdout);
- }
- /* Handle null line to avoid trapping on first command in table */
- static
- int
- donothing(argc,argv)
- int argc;
- char *argv[];
- {
- return 0;
- }
- /* Translate 'cd' to 'cwd' for convenience */
- static
- int
- doftpcd(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftp *ftp;
-
- ftp = current->cb.ftp;
- return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
- }
- /* Translate 'mkdir' to 'xmkd' for convenience */
- static
- int
- domkdir(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftp *ftp;
-
- ftp = current->cb.ftp;
- return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
- }
- /* Translate 'rmdir' to 'xrmd' for convenience */
- static
- int
- dormdir(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftp *ftp;
-
- ftp = current->cb.ftp;
- return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
- }
- /* Handle "type" command from user */
- static
- int
- dotype(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftp *ftp;
-
- ftp = current->cb.ftp;
- if(argc < 2){
- switch(ftp->type){
- case IMAGE_TYPE:
- printf("Image\n");
- break;
- case ASCII_TYPE:
- printf("Ascii\n");
- break;
- }
- return 0;
- }
- switch(*argv[1]){
- case 'i':
- case 'b':
- ftp->type = IMAGE_TYPE;
- sndftpmsg(ftp,"TYPE I\r\n");
- break;
- case 'a':
- ftp->type = ASCII_TYPE;
- sndftpmsg(ftp,"TYPE A\r\n");
- break;
- case 'l':
- if (argc > 2){
- ftp->type = IMAGE_TYPE;
- sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
- break;
- }
- /* no arg after TYPE L, drop in default
- instead of generating BUS ERROR on Atari ST (and others) */
- default:
- printf("Invalid type %s\n",argv[1]);
- return 1;
- }
- return 0;
- }
- /* Start receive transfer. Syntax: get <remote name> [<local name>] */
- static
- doget(argc,argv)
- int argc;
- char *argv[];
- {
- void ftpdr(),ftpcds();
- char *remotename,*localname;
- register struct ftp *ftp;
- char *mode;
-
- if((ftp = current->cb.ftp) == NULLFTP){
- printf(notsess);
- return 1;
- }
- remotename = argv[1];
- if(argc < 3)
- localname = remotename;
- else
- localname = argv[2];
-
- if(ftp->fp != NULLFILE && ftp->fp != stdout)
- fclose(ftp->fp);
-
- if(ftp->type == IMAGE_TYPE)
- mode = binmode[WRITE_BINARY];
- else
- mode = "w";
-
- if((ftp->fp = fopen(localname,mode)) == NULLFILE){
- printf(cantwrite,localname);
- return 1;
- }
- ftp->state = RECEIVING_STATE;
- ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
-
- /* Generate the command to start the transfer */
- return sndftpmsg(ftp,"RETR %s\r\n",remotename);
- }
- /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
- static
- dolist(argc,argv)
- int argc;
- char *argv[];
- {
- void ftpdr(),ftpcds();
- register struct ftp *ftp;
-
- if((ftp = current->cb.ftp) == NULLFTP){
- printf(notsess);
- return 1;
- }
- if(ftp->fp != NULLFILE && ftp->fp != stdout)
- fclose(ftp->fp);
-
- if(argc < 3){
- ftp->fp = stdout;
- } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
- printf(cantwrite,argv[2]);
- return 1;
- }
- ftp->state = RECEIVING_STATE;
- ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
- /* Generate the command to start the transfer
- * It's done this way to avoid confusing the 4.2 FTP server
- * if there's no argument
- */
- if(argc > 1)
- return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
- else
- return sndftpmsg(ftp,"LIST\r\n");
- }
- /* Abbreviated (name only) list of remote directory.
- * Syntax: ls <remote directory/file> [<local name>]
- */
- static
- dols(argc,argv)
- int argc;
- char *argv[];
- {
- void ftpdr(),ftpcds();
- register struct ftp *ftp;
-
- if((ftp = current->cb.ftp) == NULLFTP){
- printf(notsess);
- return 1;
- }
- if(ftp->fp != NULLFILE && ftp->fp != stdout)
- fclose(ftp->fp);
-
- if(argc < 3){
- ftp->fp = stdout;
- } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
- printf(cantwrite,argv[2]);
- return 1;
- }
- ftp->state = RECEIVING_STATE;
- ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
- /* Generate the command to start the transfer */
- if(argc > 1)
- return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
- else
- return sndftpmsg(ftp,"NLST\r\n");
- }
- /* Start transmit. Syntax: put <local name> [<remote name>] */
- static
- doput(argc,argv)
- int argc;
- char *argv[];
- {
- void ftpdt(),ftpcds();
- char *remotename,*localname;
- char *mode;
- struct ftp *ftp;
-
- if((ftp = current->cb.ftp) == NULLFTP){
- printf(notsess);
- return 1;
- }
- localname = argv[1];
- if(argc < 3)
- remotename = localname;
- else
- remotename = argv[2];
-
- if(ftp->fp != NULLFILE && ftp->fp != stdout)
- fclose(ftp->fp);
-
- if(ftp->type == IMAGE_TYPE)
- mode = binmode[READ_BINARY];
- else
- mode = "r";
-
- if((ftp->fp = fopen(localname,mode)) == NULLFILE){
- printf(cantread,localname);
- return 1;
- }
- ftp->state = SENDING_STATE;
- ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
-
- /* Generate the command to start the transfer */
- return sndftpmsg(ftp,"STOR %s\r\n",remotename);
- }
- #if (defined(MSDOS) || defined(ATARI_ST))
- /* Transmit tree. Syntax: tput <local spec> */
- static
- dotput(argc,argv)
- int argc;
- char *argv[];
- {
- char *p;
- struct ftp *ftp;
- struct ftp_list *fl,**flp;
- int num;
- char localspec[128],localname[128],name[15];
- void ftp_dellist();
- int isdir(),iswild();
- char *basename();
-
- if((ftp = current->cb.ftp) == NULLFTP){
- printf(notsess);
- return 1;
- }
- if(ftp->fp != NULLFILE && ftp->fp != stdout)
- fclose(ftp->fp);
- ftp->fp = NULLFILE;
- ftp_dellist(ftp);
-
- /* Create a (dummy) ftp_list node containing the spec */
- if ((fl = (struct ftp_list *) calloc(1,sizeof(struct ftp_list) + strlen(argv[1]))) == NULL) {
- printf(nospace);
- return 1;
- }
- strcpy(fl->localname,argv[1]);
- cleanup_fname(fl->localname);
-
- ftp->ftp_list = fl; /* 1st item of the list */
-
- /* scan the ever-growing list until all directories are processed */
- for (flp = &fl->next; fl != NULLFTPLIST; fl = fl->next) {
- strcpy(localspec,fl->localname);
- if ((fl->isdir = isdir(localspec)) != 0) /* directory - take all it's files */
- strcat(localspec,"/*.*");
- cleanup_fname(localspec);
- if (iswild(localspec)) {
- strcpy(localname,localspec);
- p = basename(localname);
- /* scan wildcard pattern, append files to end of list */
- for (num = 0; filedir(localspec,num,name), name[0] != '\0'; num++) {
- if (name[0] == '.')
- continue; /* ignore '.' and '..' */
- strcpy(p,name); /* append name to dir */
- if ((*flp = (struct ftp_list *) calloc(1,sizeof(struct ftp_list) + strlen(localname))) == NULL) {
- printf(nospace);
- ftp_dellist(ftp);
- return 1;
- }
- strcpy((*flp)->localname,localname);
- flp = &(*flp)->next;
- }
- }
- }
- /* Remove the dummy 1st element (the spec) if not a directory */
- if (!(fl = ftp->ftp_list)->isdir && iswild(fl->localname)) {
- ftp->ftp_list = fl->next;
- free(fl);
- }
- if (ftp->ftp_list == NULLFTPLIST) {
- printf("No file %s\n",argv[1]);
- return 0;
- }
- ftp->xatr = (argc > 2); /* extra arg = use XATR */
- return donextput(ftp); /* trigger the multi-store op */
- }
- static
- donextput(ftp)
- register struct ftp *ftp;
- {
- struct ftp_list *fl;
- char *mode;
- int rv;
- void ftpdt(),ftpcds();
-
- if(ftp->state != COMMAND_STATE)
- return 1;
-
- if((fl = ftp->ftp_list) == NULLFTPLIST)
- return -1;
-
- ftp->ftp_list = fl->next;
-
- if(fl->isdir) { /* is it a directory? */
- rv = sndftpmsg(ftp,"XMKD %s\r\n",fl->localname);
- } else {
- if(ftp->type == IMAGE_TYPE)
- mode = binmode[READ_BINARY];
- else
- mode = "r";
-
- if((ftp->fp = fopen(fl->localname,mode)) == NULLFILE){
- printf(cantread,fl->localname);
- return 1;
- }
- ftp->state = SENDING_STATE;
- ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
-
- /* Generate the command to start the transfer */
- rv = sndftpmsg(ftp,"STOR %s\r\n",fl->localname);
-
- #if (defined (ATARI_ST))
- /* When required, send an XATR command (set attibutes) */
- if (ftp->xatr) {
- int attr;
- unsigned long datime;
- unsigned int info[2];
- char buf[26];
-
- if ((attr = Fattrib(fl->localname,0,0)) < 0) /* get attribs */
- attr = 0;
-
- Fdatime(info,fileno(ftp->fp),0); /* get date/time */
- # if (defined(MWC) && (MWC < 306))
- datime = info[0] | ((long) info[1] << Sixteen);
- # else
- datime = info[0] | ((long) info[1] << 16);
- # endif
-
- sprintf(buf,"%u,%u,%u,%u,%u,%u",
- hibyte(hiword(datime)),
- lobyte(hiword(datime)),
- hibyte(loword(datime)),
- lobyte(loword(datime)),
- hibyte(attr),
- lobyte(attr));
-
- rv |= sndftpmsg(ftp,"XATR %s\r\n",buf);
- }
- #endif
- }
-
- free(fl);
- return rv;
- }
- #endif
- /* Abort a GET or PUT operation in progress. Note: this will leave
- * the partial file on the local or remote system
- */
- doabort(argc,argv)
- int argc;
- char *argv[];
- {
- register struct ftp *ftp;
-
- ftp = current->cb.ftp;
-
- /* Close the local file */
- if(ftp->fp != NULLFILE && ftp->fp != stdout)
- fclose(ftp->fp);
- ftp->fp = NULLFILE;
- #if (defined(MSDOS) || defined(ATARI_ST))
- ftp_dellist(ftp);
- #endif
- switch(ftp->state){
- case SENDING_STATE:
- /* Send a premature EOF.
- * Unfortunately we can't just reset the connection
- * since the remote side might end up waiting forever
- * for us to send something.
- */
- close_tcp(ftp->data);
- printf("Put aborted\n");
- break;
- case RECEIVING_STATE:
- /* Just exterminate the data channel TCB; this will
- * generate a RST on the next data packet which will
- * abort the sender
- */
- del_tcp(ftp->data);
- ftp->data = NULLTCB;
- printf("Get aborted\n");
- break;
- }
- ftp->state = COMMAND_STATE;
- fflush(stdout);
- }
- /* create data port, and send PORT message */
- static
- ftpsetup(ftp,recv,send,state)
- struct ftp *ftp;
- void (*send)();
- void (*recv)();
- void (*state)();
- {
- struct socket lsocket;
- struct mbuf *bp;
-
- lsocket.address = ip_addr;
- lsocket.port = lport++;
-
- /* Compose and send PORT a,a,a,a,p,p message */
-
- if((bp = alloc_mbuf(35)) == NULLBUF){ /* 5 more than worst case */
- printf(nospace);
- return;
- }
- /* I know, this looks gross, but it works! */
- sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
- hibyte(hiword(lsocket.address)),
- lobyte(hiword(lsocket.address)),
- hibyte(loword(lsocket.address)),
- lobyte(loword(lsocket.address)),
- hibyte(lsocket.port),
- lobyte(lsocket.port));
- bp->cnt = strlen(bp->data);
- send_tcp(ftp->control,bp);
-
- /* Post a listen on the data connection */
- ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
- recv,send,state,ftp->control->tos,(char *)ftp);
- }
- /* FTP Client Control channel Receiver upcall routine */
- void
- ftpccr(tcb,cnt)
- register struct tcb *tcb;
- int16 cnt;
- {
- struct mbuf *bp;
- struct ftp *ftp;
- extern int flowctrl,ttydriv();
-
- if((ftp = (struct ftp *)tcb->user) == NULLFTP){
- /* Unknown connection; kill it */
- close_tcp(tcb);
- return;
- }
- /* Hold output if we're not the current session, and while typing */
- if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp
- || (flowctrl && ttydriv(-1,NULL)))
- return;
-
- if(recv_tcp(tcb,&bp,cnt) > 0){
- while(bp != NULLBUF){
- fwrite(bp->data,1,(unsigned)bp->cnt,stdout);
- if(current->record != NULLFILE)
- fwrite(bp->data,1,(unsigned)bp->cnt,current->record);
- bp = free_mbuf(bp);
- }
- fflush(stdout);
- }
- #if (defined(MSDOS) || defined(ATARI_ST))
- if (ftp->state == COMMAND_STATE)
- donextput(ftp);
- #endif
- }
-
- /* FTP Client Control channel State change upcall routine */
- static
- void
- ftpccs(tcb,old,new)
- register struct tcb *tcb;
- char old,new;
- {
- void ftp_delete();
- struct ftp *ftp;
- char notify = 0;
- extern char *tcpstates[];
- extern char *reasons[];
- extern char *unreach[];
- extern char *exceed[];
-
- /* Can't add a check for unknown connection here, it would loop
- * on a close upcall! We're just careful later on.
- */
- ftp = (struct ftp *)tcb->user;
-
- if(current != NULLSESSION && current->cb.ftp == ftp)
- notify = 1;
-
- switch(new){
- case CLOSE_WAIT:
- if(notify)
- printf("%s\n",tcpstates[new]);
- close_tcp(tcb);
- break;
- case CLOSED: /* heh heh */
- if(notify){
- printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
- if(tcb->reason == NETWORK){
- switch(tcb->type){
- case DEST_UNREACH:
- printf(": %s unreachable",unreach[tcb->code]);
- break;
- case TIME_EXCEED:
- printf(": %s time exceeded",exceed[tcb->code]);
- break;
- }
- }
- printf(")\n");
- cmdmode();
- }
- del_tcp(tcb);
- if(ftp != NULLFTP)
- ftp_delete(ftp);
- break;
- default:
- if(notify)
- printf("%s\n",tcpstates[new]);
- break;
- }
- if(notify)
- fflush(stdout);
- }
- /* FTP Client Data channel State change upcall handler */
- static
- void
- ftpcds(tcb,old,new)
- struct tcb *tcb;
- char old,new;
- {
- struct ftp *ftp;
- unsigned long xfertime;
-
- if((ftp = (struct ftp *)tcb->user) == NULLFTP){
- /* Unknown connection, kill it */
- close_tcp(tcb);
- return;
- }
- switch(new){
- case ESTABLISHED:
- /* Start the timer so we can print the effective speed */
- ftp->xfertime.start = MAX_TIME;
- start_timer(&ftp->xfertime);
- break;
-
- case FINWAIT2:
- case TIME_WAIT:
- if(ftp->state == SENDING_STATE){
- /* We've received an ack of our FIN, so
- * return to command mode
- */
- ftp->state = COMMAND_STATE;
- if(current != NULLSESSION && current->cb.ftp == ftp){
- printf("Put complete, %lu bytes sent",
- tcb->snd.una - tcb->iss - 2);
- if ((xfertime = TICK2MS(ftp->xfertime.start - ftp->xfertime.count)) != 0)
- printf(" in %lu seconds (%lu bits/s)",
- (xfertime + 500L) / 1000L,
- ((unsigned long) 8000 * (tcb->snd.una - tcb->iss - 2)) / xfertime);
- stop_timer(&ftp->xfertime);
- printf("\n");
- fflush(stdout);
- }
- }
- break;
- case CLOSE_WAIT:
- close_tcp(tcb);
- if(ftp->state == RECEIVING_STATE){
- /* End of file received on incoming file */
- #ifdef CPM
- if(ftp->type == ASCII_TYPE)
- putc(CTLZ,ftp->fp);
- #endif
- if(ftp->fp != stdout)
- fclose(ftp->fp);
- ftp->fp = NULLFILE;
- ftp->state = COMMAND_STATE;
- if(current != NULLSESSION && current->cb.ftp == ftp){
- printf("Get complete, %lu bytes received",
- tcb->rcv.nxt - tcb->irs - 2);
- if ((xfertime = TICK2MS(ftp->xfertime.start - ftp->xfertime.count)) != 0)
- printf(" in %lu seconds (%lu bits/s)",
- (xfertime + 500L) / 1000L,
- ((unsigned long) 8000 * (tcb->rcv.nxt - tcb->irs - 2)) / xfertime);
- stop_timer(&ftp->xfertime);
- printf("\n");
- fflush(stdout);
- }
- }
- break;
- case CLOSED:
- ftp->data = NULLTCB;
- del_tcp(tcb);
- break;
- }
- }
- /* Send a message on the control channel */
- /*VARARGS*/
- static
- int
- sndftpmsg(ftp,fmt,arg)
- struct ftp *ftp;
- char *fmt;
- char *arg;
- {
- struct mbuf *bp;
- int16 len;
-
- len = strlen(fmt) + strlen(arg) + 10; /* fudge factor */
- if((bp = alloc_mbuf(len)) == NULLBUF){
- printf(nospace);
- return 1;
- }
- sprintf(bp->data,fmt,arg);
- bp->cnt = strlen(bp->data);
- send_tcp(ftp->control,bp);
- return 0;
- }
-